package general.appClass;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.*;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

public class StatisticApp extends HttpServlet {
	private static final long serialVersionUID = 6547884980631863249L;

	/**
	 * statistic.xml file write-lock
	 */
	public static boolean fileLock = false;

	/**
	 * Local path of statistic.xml's Folder
	 */
	private static String xmlFolderPath;

	/**
	 * Local path of statistic.xml
	 */
	private static String xmlFilePath;

	/**
	 * Query the statistic information,before they can be writen into xml file.
	 */
	public static ArrayList<Data> dataQuery = new ArrayList<Data>();

	/**
	 * 
	 */
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		this.doPost(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		String action = request.getParameter("action");
		//System.out.println("StatisticApp?action=" + action);

		// Get real path of statistic.xml file under WebContent/xml.
		xmlFolderPath = request.getServletContext().getRealPath("/") + "xml"
				+ System.getProperty("file.separator");
		xmlFilePath = xmlFolderPath + "statistic.xml";
		// if action="show",record the current dataQuery into statistic.xml and
		// convert to .htm file
		if (action != null && action.equalsIgnoreCase("show")) {
			record();
			// type = "thumb" or "upload"?
			String type = request.getParameter("type");
			String url = response.encodeURL("/xml/" + type + "Result.htm");
			RequestDispatcher dispatcher = getServletContext()
					.getRequestDispatcher(url);
			dispatcher.forward(request, response);
		}

		// if action="renew",delete all record in statistic.xml.
		if (action != null && action.equalsIgnoreCase("renew")) {
			renew();
			String url = response.encodeURL("/xml/totalResult.htm");
			RequestDispatcher dispatcher = getServletContext()
					.getRequestDispatcher(url);
			dispatcher.forward(request, response);
		}
	}

	/**
	 * renew the statistic.xml
	 */
	public static void renew() {
		// xml doc factory
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true);
		factory.setValidating(false);
		DocumentBuilder builder = null;
		Document doc = null;
		//System.out.println(xmlFilePath);
		// xml doc builder
		try {
			builder = factory.newDocumentBuilder();
		} catch (ParserConfigurationException pce) {
			System.err.println(pce);
		}
		try {
			// xml doc
			doc = builder.parse(xmlFilePath);
			doc.normalize();
		} catch (DOMException dom) {
			System.err.println(dom.getMessage());
		} catch (IOException ioe) {
			System.err.println(ioe);
		} catch (SAXException e) {
			e.printStackTrace();
		}
		// root element <statistic>
		Element statistic = doc.getDocumentElement();
		NodeList children = statistic.getChildNodes();

		// remove all children of <statistic>
		for (int i = 0; i < children.getLength();) {
			statistic.removeChild(children.item(i));
		}
		// write changed statistic information into file
		TransformerFactory tFactory = TransformerFactory.newInstance();
		Transformer transformer = null;
		try {
			transformer = tFactory.newTransformer();
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		}
		DOMSource source = new DOMSource(doc);
		StreamResult result = new StreamResult(new java.io.File(xmlFilePath));
		try {
			transformer.transform(source, result);
		} catch (TransformerException e) {
			e.printStackTrace();
		}

		// run xslt processor,create new totalResult.htm
		xsltProcessor("total");
	}

	// put a piece of statistic data into statistic variable - dataQuery.
	public static void query(String type, String mode, long size, long time,
			int count) {
		Data data = new Data(type, mode, size, time, count);
		dataQuery.add(data);
	}

	// record statistic data of dataQuery into statistic.xml
	public static void record() {
		if (dataQuery.size() == 0) {
			return;
		}
		while (fileLock) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		fileLock = true;

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true);
		factory.setValidating(false);
		DocumentBuilder builder = null;
		Document doc = null;
		//System.out.println(xmlFilePath);
		try {
			builder = factory.newDocumentBuilder();
		} catch (ParserConfigurationException pce) {
			System.err.println(pce);
		}
		try {
			doc = builder.parse(xmlFilePath);
			doc.normalize();
		} catch (DOMException dom) {
			System.err.println(dom.getMessage());
		} catch (IOException ioe) {
			System.err.println(ioe);
		} catch (SAXException e) {
			e.printStackTrace();
		}

		Element statistic = doc.getDocumentElement();
		for (int i = 0; i < dataQuery.size(); i++) {
			String mode = dataQuery.get(i).mode;
			String type = dataQuery.get(i).type;
			long size = 0;
			long time = 0;
			int count = 0;
			while (i < dataQuery.size()
					&& dataQuery.get(i).mode.equalsIgnoreCase(mode)
					&& dataQuery.get(i).type.equalsIgnoreCase(type)) {
				size += dataQuery.get(i).size;
				time += dataQuery.get(i).time;
				count += dataQuery.get(i).count;
				i++;
			}
			Element element = doc.createElement(type);
			element.setAttribute("mode", mode);
			element.setAttribute("size", Long.toString(size));
			element.setAttribute("time", Long.toString(time));
			element.setAttribute("count", Integer.toString(count));

			Date date = new Date();
			SimpleDateFormat formatter = new SimpleDateFormat(
					"yyyy.MM.dd HH:mm:ss");

			element.setAttribute("date", Long.toString(date.getTime()));
			element.setAttribute("date-string", formatter.format(date));
			statistic.appendChild(element);

			// Write changed statistic information into file
			TransformerFactory tFactory = TransformerFactory.newInstance();
			Transformer transformer = null;
			try {
				transformer = tFactory.newTransformer();
			} catch (TransformerConfigurationException e) {
				e.printStackTrace();
			}
			DOMSource source = new DOMSource(doc);
			StreamResult result = new StreamResult(
					new java.io.File(xmlFilePath));
			try {
				transformer.transform(source, result);
			} catch (TransformerException e) {
				e.printStackTrace();
			}
			xsltProcessor(type);
		}
		xsltProcessor("total");
		fileLock = false;
		dataQuery = new ArrayList<Data>();
	}

	public static void xsltProcessor(String type) {

		String inFilename = xmlFolderPath + "statistic.xml";
		String outFilename = null;
		String xslFilename = null;
		if (type.equals("thumb")) {
			outFilename = xmlFolderPath + "thumbResult.htm";
			xslFilename = xmlFolderPath + "thumbResult.xsl";
		} else if (type.equals("upload")) {
			outFilename = xmlFolderPath + "uploadResult.htm";
			xslFilename = xmlFolderPath + "uploadResult.xsl";
		} else {
			outFilename = xmlFolderPath + "totalResult.htm";
			xslFilename = xmlFolderPath + "totalResult.xsl";
		}
		try {
			// Create transformer factory
			TransformerFactory factory = TransformerFactory.newInstance();
			// Use the factory to create a template containing the xsl file
			Templates template = factory.newTemplates(new StreamSource(
					new FileInputStream(xslFilename)));
			// Use the template to create a transformer
			Transformer xformer = template.newTransformer();

			// Prepare the input and output files
			FileInputStream fis = new FileInputStream(inFilename);
			FileOutputStream fos = new FileOutputStream(outFilename);
			Source source = new StreamSource(fis);
			Result result = new StreamResult(fos);
			fos.flush();
			// Apply the xsl file to the source file and write the result to
			// the output file
			xformer.transform(source, result);
		} catch (FileNotFoundException e) {
			// File not found
		} catch (TransformerConfigurationException e) {
			// An error occurred in the XSL file
		} catch (TransformerException e) {
			// An error occurred while applying the XSL file
			// Get location of error in input file
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

class Data {
	String type;
	String mode;
	long size = 0;
	long time = 0;
	int count = 0;

	public Data(String type, String mode, long size, long time, int count) {
		this.type = type;
		this.mode = mode;
		this.time = time;
		this.size = size;
		this.count = count;
	}
}
